package com.pipayshop.service.Impl;


import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pipayshop.config.CommonConfig;
import com.pipayshop.entity.AccountInfo;
import com.pipayshop.entity.TradinJournal;
import com.pipayshop.entity.TradinOrder;
import com.pipayshop.entity.TradinPost;
import com.pipayshop.entity.dto.PayPalDTO;
import com.pipayshop.entity.vo.TradinOrderDetailVO;
import com.pipayshop.entity.vo.TradinOrderListVO;
import com.pipayshop.exception.BusinessException;
import com.pipayshop.mapper.*;
import com.pipayshop.service.TradinOrderService;
import com.pipayshop.service.UserAccountInfoService;
import com.pipayshop.utils.Constants;
import com.pipayshop.utils.FileUploadUtil;
import com.pipayshop.utils.StringUtil;
import com.pipayshop.utils.TokenUtil;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import net.coobird.thumbnailator.Thumbnails;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.math.BigDecimal;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author wzx
 * @since 2023-09-19
 */
@Service
public class TradinOrderServiceImpl extends ServiceImpl<TradinOrderMapper, TradinOrder> implements TradinOrderService {

    @Resource
    private TradinOrderMapper tradinOrderMapper;
    @Resource
    private TradinPostMapper tradinPostMapper;
    @Resource
    private UserAccountInfoMapper accountInfoMapper;
    @Resource
    private UserAccountInfoService accountInfoService;
    @Resource
    private TradinJournalMapper tradinJournalMapper;
    @Resource
    private ImageMapper imageMapper;

    @Resource
    private RabbitTemplate rabbitTemplate;

    private final static Logger LOGGER = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateTradeOrder(String tradinId, String buyerId) {
        if (StringUtils.isBlank(tradinId) || StringUtils.isBlank(buyerId)) {
            throw new BusinessException("invalid_parameter");
        }
        // 获取发布详情
        TradinPost tradinPost = tradinPostMapper.selectOne(new QueryWrapper<TradinPost>()
                .eq("tradin_id", tradinId));
        // 判断是否有人已经下单了
        if (tradinPost.getStatus() != 0) {
            throw new BusinessException("已经下单了");
        }

        // 乐观锁基于版本号更新
        // 更新版本号
        int update = tradinPostMapper.update(null, new UpdateWrapper<TradinPost>()
//                .setSql("status=status+1")
                .setSql("status=1")
                .eq("tradin_id", tradinId)
                .eq("status", tradinPost.getStatus()));
        if (update < 1) {
            // 更新版本号失败,已经给人抢先下单了，慢了一步返回null
            throw new BusinessException("已经下单了");
        }
        // 生成订单
        String orderId = StringUtil.generateShortId();
        TradinOrder tradinOrder = new TradinOrder();
        tradinOrder.setOrderId(orderId)
                .setSellerId(tradinPost.getPublisherUid())
                .setBuyerId(buyerId)
                .setTypeId(tradinPost.getTypeId())
                .setPiBalance(tradinPost.getPiBalance())
                .setPointBalance(tradinPost.getPointBalance())
                .setCreateTime(new Date())
                .setUpdateTime(new Date())
                .setId(System.currentTimeMillis());
        if (tradinPost.getTypeId() == 0 || tradinPost.getTypeId() == 2) {
            // 订单状态为0:开始交易
            tradinOrder.setStatus(0);
        } else if (tradinPost.getTypeId() == 1) {
            // 订单状态为1:提交积分/地址
            tradinOrder.setPiAddress(tradinPost.getPiAddress())
                    .setStatus(1);
        }
        int insert = tradinOrderMapper.insert(tradinOrder);
        // 改变交易状态为交易中，
        tradinPost.setStatus(2);
        tradinPost.setOrderId(orderId);
        insert += tradinPostMapper.updateById(tradinPost);
        if (insert < 2) {
            throw new BusinessException("更新/插入失败");
        }
        return orderId;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean upLoadPointBalance(String token) {
        // 解析token
        Claims dataFromToken = TokenUtil.getDataFromToken(token);
        Integer typeId = dataFromToken.get("typeId", Integer.class);
        String orderId = dataFromToken.get("orderId", String.class);
        if (StringUtils.isBlank(orderId) || typeId == null) {
            throw new BusinessException("invalid_parameter");
        }
        // 此交易发布者需要积分
        if (typeId == 0) {
            String buyerId = dataFromToken.get("buyerId", String.class);
            String piAddress = dataFromToken.get("piAddress", String.class);
            if (piAddress.isEmpty()) {
                throw new BusinessException("pi地址不能为空");
            }
            // 查询订单信息
            TradinOrder tradinOrder = tradinOrderMapper.selectOne(new QueryWrapper<TradinOrder>()
                    .eq("order_id", orderId));
            if (tradinOrder == null) {
                throw new BusinessException("订单不存在");
            }
            if (!typeId.equals(tradinOrder.getTypeId())) {
                throw new BusinessException("类型不一样");
            }
            // 交易需要的积分
            BigDecimal pointBalance = tradinOrder.getPointBalance();
            //冻结积分
            accountInfoService.freezePoints(buyerId, pointBalance);
            // 改变订单状态为提交积分和写入钱包地址
            tradinOrder.setPiAddress(piAddress);
            tradinOrder.setStatus(1);
            int update = tradinOrderMapper.updateById(tradinOrder);
            if (update < 1) {
                throw new BusinessException("update_failed");
            }
            return true;
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean upLoadImg(MultipartFile file, String token) {
        if (file.isEmpty()) {
            throw new BusinessException("文件不能为空");
        }
        String fileName = file.getOriginalFilename();
        if ("".equals(fileName)) {
            throw new BusinessException("文件名不能为空");
        }

        // 解析token
        Claims dataFromToken = TokenUtil.getDataFromToken(token);
        Integer typeId = dataFromToken.get("typeId", Integer.class);
        String orderId = dataFromToken.get("orderId", String.class);
        String userId = dataFromToken.get("userId", String.class);
        if (StringUtils.isBlank(orderId) || StringUtils.isBlank(userId) || typeId == null) {
            throw new BusinessException("invalid_parameter");
        }
        String confirmUserId = null;

        // 查询订单，判断交易类型是否一致
        TradinOrder tradinOrder = tradinOrderMapper.selectOne(new QueryWrapper<TradinOrder>()
                .eq("order_id", orderId));
        if (tradinOrder == null) {
            throw new BusinessException("订单不存在");
        }
        if (!Objects.equals(typeId, tradinOrder.getTypeId())) {
            throw new BusinessException("类型不一样");
        }
        // 判断是否是对应的人提交凭证
        if (tradinOrder.getTypeId() == 0) {

            if (!userId.equals(tradinOrder.getSellerId())) {
                throw new BusinessException("提交凭证对象不对");
            }
            confirmUserId = tradinOrder.getBuyerId();
        } else if (tradinOrder.getTypeId() == 1 || tradinOrder.getTypeId() == 2) {

            if (!userId.equals(tradinOrder.getBuyerId())) {
                throw new BusinessException("提交凭证对象不对");
            }
            confirmUserId = tradinOrder.getSellerId();
        }
        // 上传图片和改订单状态为
//        String ThumbnailPath = imageUpload(file, fileName);
//        tradinOrder.setImageUrl("/images/tradin_post_certificate/" + ThumbnailPath);
        //使用OSS
        String imageId = FileUploadUtil.allUploadImageData(file, imageMapper, FileUploadUtil.ECHANGE_IMG, null);
        tradinOrder.setImageUrl(imageId);
        // 2:提交凭证图片
        tradinOrder.setStatus(2);
        // 更改订单状态和提交图片
        int update = tradinOrderMapper.updateById(tradinOrder);

        if (update < 1) {
            throw new BusinessException("提交失败");
        }

        Map<String, String> confirmOrder = new HashMap<>();
        confirmOrder.put("userId", confirmUserId);
        confirmOrder.put("orderId", orderId);
        String orderMap = JSON.toJSONString(confirmOrder);
        rabbitTemplate.convertAndSend(Constants.DELAYED_EXCHANGE_NAME, Constants.DELAYED_ROUTING_KEY, orderMap,
                correlationData -> {
                    // 自动确认订单设为7天
                    correlationData.getMessageProperties().setDelay(604800000);
                    return correlationData;
                });
        return true;
    }

    //图片上传
    private static @NotNull String imageUpload(MultipartFile file, String fileName) {
        String separator = File.separator;
        String postStr = fileName.substring(fileName.lastIndexOf(".") + 1);
        String preStr = StringUtil.generateShortId();
        fileName = "temp-" + preStr + "." + postStr;
        String ThumbnailPath = preStr + "." + postStr;
        File readPath = new File(Constants.CERTIFICATE_IMAG_PATH + separator);
        if (!readPath.isDirectory()) {
            readPath.mkdirs();
        }
        // 将文件复制到指定路径
        File destFile = new File(readPath.getAbsolutePath() + separator + fileName);
        try {
            // 移动temp图片到目标文件夹
            FileCopyUtils.copy(file.getBytes(), destFile);
            String absolutePath = new File(Constants.CERTIFICATE_IMAG_PATH + separator + ThumbnailPath).getAbsolutePath();
            // 压缩图片
            Thumbnails.of(Constants.CERTIFICATE_IMAG_PATH + separator + fileName)
                    .size(300, 300)
                    .outputFormat(postStr)
                    .toFile(absolutePath);
            // 删除temp图片
            if (destFile.exists()) {
                destFile.delete();
            }

        } catch (Exception e) {
            if (destFile.exists()) {
                destFile.delete();
                throw new BusinessException("提交失败");
            }
            e.printStackTrace();
        }
        return ThumbnailPath;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean confirmTransaction(String token) {

        // 解析token
        Claims dataFromToken = TokenUtil.getDataFromToken(token);
        String userId = dataFromToken.get("userId", String.class);
        String orderId = dataFromToken.get("orderId", String.class);
        if (StringUtils.isBlank(userId) || StringUtils.isBlank(orderId)) {
            throw new BusinessException("invalid_parameter");
        }
        // 查询订单
        TradinOrder tradinOrder = tradinOrderMapper.selectOne(new QueryWrapper<TradinOrder>().eq("order_id", orderId));
        if (tradinOrder == null) {
            throw new BusinessException("订单不存在");
        }
        BigDecimal pointBalance = tradinOrder.getPointBalance();
        AccountInfo buyerAccountInfo = null;
        AccountInfo sellerAccountInfo = null;
        TradinJournal tradinJournal = new TradinJournal();
        String journalId = StringUtil.generateShortId();
        tradinJournal.setJournalId(journalId)
                .setPublisherUid(tradinOrder.getSellerId())
                .setTraderUid(tradinOrder.getBuyerId())
                .setTypeId(tradinOrder.getTypeId())
                .setPointBalance(pointBalance)
                .setPiBalance(tradinOrder.getPiBalance());
        // 交易类型为发布者需要pi币
        if (tradinOrder.getTypeId() == 1 || tradinOrder.getTypeId() == 2) {
            // 查看userId是否是这次交易的发布者uid
            if (!tradinOrder.getSellerId().equals(userId)) {
                throw new BusinessException("卖家uid不匹配");
            }
            // 获取卖家账户 并扣除总积分和冻结积分
            BigDecimal sellerOldPointBalance = accountInfoService.removeFrozen(tradinOrder.getSellerId(), pointBalance);
            BigDecimal sellerNewPointBalance = sellerOldPointBalance.subtract(pointBalance);
            // 获取买家账户 加积分和可用积分
            buyerAccountInfo = accountInfoMapper.selectOne(new QueryWrapper<AccountInfo>().eq("uid", tradinOrder.getBuyerId()));
            BigDecimal buyerOldPointBalance = buyerAccountInfo.getPointBalance();
            BigDecimal buyerNewPointBalance = buyerOldPointBalance.add(pointBalance);
            accountInfoService.addAccount(tradinOrder.getBuyerId(), pointBalance);
            // 添加日志
            tradinJournal.setPublisherPointBalanceBefore(sellerOldPointBalance)
                    .setPublisherPointBalanceAfter(sellerNewPointBalance)
                    .setTraderPointBalanceBefore(buyerOldPointBalance)
                    .setTraderPointBalanceAfter(buyerNewPointBalance);
            // 改变订单状态为交易完成
            tradinOrder.setStatus(3)
                    .setJournalId(journalId);

            // 交易类型为发布者需要积分
        } else if (tradinOrder.getTypeId() == 0) {
            // 查看userId是否是这次交易的买家uid
            if (!tradinOrder.getBuyerId().equals(userId)) {
                throw new BusinessException("买家uid不匹配");
            }
            // 获取买家账户 并扣除总积分和冻结积分
            BigDecimal buyerOldPointBalance = accountInfoService.removeFrozen(tradinOrder.getBuyerId(), pointBalance);
            BigDecimal buyerNewPointBalance = buyerOldPointBalance.subtract(pointBalance);
            // 获取卖家账户 并加积分和可用积分
            sellerAccountInfo = accountInfoMapper.selectOne(new QueryWrapper<AccountInfo>().eq("uid", tradinOrder.getSellerId()));
            BigDecimal sellerOldPointBalance = sellerAccountInfo.getPointBalance();
            BigDecimal sellerNewPointBalance = sellerOldPointBalance.add(pointBalance);
            accountInfoService.addAccount(tradinOrder.getSellerId(), pointBalance);
            // 添加日志
            tradinJournal.setPublisherPointBalanceBefore(sellerOldPointBalance)
                    .setPublisherPointBalanceAfter(sellerNewPointBalance)
                    .setTraderPointBalanceBefore(buyerOldPointBalance)
                    .setTraderPointBalanceAfter(buyerNewPointBalance);
            // 改变订单状态为交易完成
            tradinOrder.setStatus(3)
                    .setJournalId(journalId);

        }
        // 添加日志
        int update = tradinJournalMapper.insert(tradinJournal);
        // 更新订单为3:交易完成 和日志外键
        update += tradinOrderMapper.updateById(tradinOrder);
        if (update < 2) {
            throw new BusinessException("update_failed");
        }
        return true;
    }

    @Override
    public List<TradinOrderListVO> selectTradinyOrderByUid(String token) {
        // 解析token
        Claims dataFromToken = TokenUtil.getDataFromToken(token);
        String userId = dataFromToken.get("userId", String.class);
        Integer page = dataFromToken.get("page", Integer.class);
        Integer limit = dataFromToken.get("limit", Integer.class);
        return tradinOrderMapper.selectTradinyOrderByUid(userId, (page - 1) * limit, limit);
    }

    @Override
    public TradinOrderDetailVO selectTradinOrderDetail(String orderId) {
        if (StringUtils.isBlank(orderId)) {
            throw new BusinessException("invalid_parameter");
        }
        TradinOrderDetailVO tradinOrderDetailVO = tradinOrderMapper.selectTradinOrderDetail(orderId);
        if (tradinOrderDetailVO == null) {
            throw new BusinessException("数据异常");
        }
        if (tradinOrderDetailVO.getSellerImageUrl() != null) {
            tradinOrderDetailVO.setSellerImageUrl(imageMapper.selectPath(tradinOrderDetailVO.getSellerImageUrl()));
        }
        if (tradinOrderDetailVO.getBuyerImageUrl() != null) {
            tradinOrderDetailVO.setBuyerImageUrl(imageMapper.selectPath(tradinOrderDetailVO.getBuyerImageUrl()));
        }
        return tradinOrderDetailVO;

    }

    @Resource
    private CommonConfig commonConfig;

    private String getAuth(String client_id, String app_secret) {
        String auth = client_id + ":" + app_secret;
        return Base64.getEncoder().encodeToString(auth.getBytes());
    }

    public String generateAccessToken() {
        String auth = getAuth(
                commonConfig.getPAYPAL_CLIENT_ID(),

                commonConfig.getPAYPAL_APP_SECRET()
        );
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.set("Authorization", "Basic " + auth);

        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
        HttpEntity<?> request = new HttpEntity<>(requestBody, headers);
        requestBody.add("grant_type", "client_credentials");

        ResponseEntity<String> response = restTemplate.postForEntity(
                Constants.PAYPAL_BASE_PATH + "/v1/oauth2/token",
                request,
                String.class
        );

        if (response.getStatusCode() == HttpStatus.OK) {
            LOGGER.log(Level.INFO, "GET TOKEN: SUCCESSFUL!");
            return new JSONObject(response.getBody()).getString("access_token");
        } else {
            LOGGER.log(Level.SEVERE, "GET TOKEN: FAILED!");
            return "Unavailable to get ACCESS TOKEN, STATUS CODE " + response.getStatusCode();
        }
    }

    @Override

    public Object capturePayment(String orderId) {
        if (StringUtils.isBlank(orderId)) {
            throw new BusinessException("invalid_parameter");
        }
        log.error("capturePayment===========================================start");
        String accessToken = generateAccessToken();
        HttpHeaders headers = new HttpHeaders();
        RestTemplate restTemplate = new RestTemplate();

        headers.set("Authorization", "Bearer " + accessToken);
        headers.add("Content-Type", "application/json");
        headers.add("Accept", "application/json");
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> entity = new HttpEntity<String>(null, headers);

        ResponseEntity<Object> response = restTemplate.exchange(
                Constants.PAYPAL_BASE_PATH + "/v2/checkout/orders/" + orderId + "/capture",
                HttpMethod.POST,
                entity,
                Object.class
        );
        log.error("capturePayment===========================================end");
        log.error("response===========================================" + response.getBody());
        if (response.getStatusCode() == HttpStatus.CREATED) {
            LOGGER.log(Level.INFO, "ORDER CREATED");
            return response.getBody();
        } else {
            LOGGER.log(Level.INFO, "FAILED CREATING ORDER");
            return "Unavailable to get CREATE AN ORDER, STATUS CODE " + response.getStatusCode();
        }
    }

    @Override
    public Object createOrder(PayPalDTO payPalDTO) {
        if (payPalDTO == null) {
            throw new BusinessException("invalid_parameter");
        }
        log.error("createOrder===========================================start");
        String accessToken = generateAccessToken();
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + accessToken);
        headers.add("Content-Type", "application/json");
        headers.add("Accept", "application/json");
        headers.setContentType(MediaType.APPLICATION_JSON);

        //JSON String
        String requestJson = "{\"intent\":\"CAPTURE\",\"purchase_units\":[{\"amount\":{\"currency_code\":\"USD\",\"value\":\"1.00\"}}]}";
        log.error("requestJson===========================================" + requestJson);
        String updatedValue = payPalDTO.getPayPalMoney().toString();
        requestJson = requestJson.replace("1.00", updatedValue);
        log.error("requestJson===========================================start" + requestJson);
        HttpEntity<String> entity = new HttpEntity<String>(requestJson, headers);
        log.error("entity===========================================start" + entity);
        ResponseEntity<Object> response = restTemplate.exchange(
                Constants.PAYPAL_BASE_PATH + "/v2/checkout/orders",
                HttpMethod.POST,
                entity,
                Object.class
        );
        log.error("response===========================================start" + response);
        log.error("createOrder===========================================end");
        log.error("response===============================================" + response.getBody());
        if (response.getStatusCode() == HttpStatus.CREATED) {
            LOGGER.log(Level.INFO, "ORDER CAPTURE");

            return response.getBody();
        } else {
            LOGGER.log(Level.INFO, "FAILED CAPTURING ORDER");
            return "Unavailable to get CAPTURE ORDER, STATUS CODE " + response.getStatusCode();
        }
    }
}
